-module(worker).
-include("definitions.hrl").
%% API
-export([start/4, stop/1, getRestoredState/2, getRestoredStateFromEts/1]).

%WasFault => восстановить состояние, не использовать параметр TestId
start(TestExecutorPid, DeviceId, TestId, WasFault) ->
  random:seed(erlang:now()),
  State = case WasFault of
            true ->
              getRestoredStateFromEts(DeviceId);
  %getRestoredState(DeviceId);
            false ->
              %{TestIdInt, _} = string:to_integer(hd(TestId)),
              getRestoredState(DeviceId, TestId)
          end,

  Test = test_storage:getTest(State#stateRecord.testId),
  Bits = Test#testCase.bits,

  SkippedIfRunBefore = if
                         State#stateRecord.bitsRun > 0 ->
                           lists:nthtail(State#stateRecord.bitsRun, Bits);
                         true -> Bits
                       end,
  runTest(TestExecutorPid, SkippedIfRunBefore, State).

runTest(TestExecutorPid, [], State) ->
  NewState = State#stateRecord{completed = true, answer = State#stateRecord.answer},
  TestExecutorPid ! {ok, NewState};

runTest(TestExecutorPid, [H | T], State) ->
  NewState = processByte(H, State, TestExecutorPid),
  receive
    stop ->
      exit(ok)
  after
    30 ->
      SleepTime = ?SLEEP_MSES + random:uniform(100),
      timer:sleep(SleepTime),
      runTest(TestExecutorPid, T, NewState)
  end.

processByte(InputByte, State, TestExecutorPid) ->
  Rand = random:uniform(10) + (InputByte - 5),
  if
    InputByte == Rand ->
      Fails = State#stateRecord.fails + 1,
      FailState = State#stateRecord{fails = Fails},
      try TestExecutorPid ! {update, FailState} of
        _ -> ok
      catch
        error :_ -> ok
      end,
      exit(self(), errorDevice);
    true ->
      Answer = getAnswer(InputByte),
      BitsRun = State#stateRecord.bitsRun + 1,
      NewState = State#stateRecord{answer = State#stateRecord.answer ++ Answer, bitsRun = BitsRun},
      try TestExecutorPid ! {update, NewState} of
        _ -> ok
      catch
        error :_ -> ok
      end,
      NewState
  end.

getAnswer(X) -> [X].

%-spec getRestoredState/1 :: (integer()) -> #stateRecord
%старая версия, нагружала мнезию
getRestoredState(DeviceId) ->
  Last = test_storage:getLastStateForDevice(DeviceId),
  ReturnState = case Last#stateRecord.id of
                  -1 ->
                    NextId = test_storage:getNextId(),
                    AllTests = test_storage:getAllTestIds(),
                    Test = getRandomTest(AllTests),
                    State = #stateRecord{id = NextId, deviceId = DeviceId, testId = Test#testCase.id},
                    state_storage:updateState(State),
                    State;%инициализировались
                  _ -> Last
                end,
  ReturnState.

%-spec getRestoredState/1 :: (integer()) -> #stateRecord
getRestoredStateFromEts(DeviceId) ->
  Last = state_storage:getLastForDevice(DeviceId),
  %test_storage:getLastStateForDevice(DeviceId),
  ReturnState = case Last#stateRecord.id of
                  -1 ->
                    NextId = test_storage:getNextId(),
                    AllTests = test_storage:getAllTestIds(),
                    Test = getRandomTest(AllTests),
                    State = #stateRecord{id = NextId, deviceId = DeviceId, testId = Test#testCase.id},
                    state_storage:updateState(State),
                    State;%инициализировались
                  _ -> Last
                end,
  ReturnState.


getRestoredState(DeviceId, TestId) ->
  Last = test_storage:getLastStateForDevice(DeviceId),
  ReturnState = case Last#stateRecord.id of
                  -1 ->
                    NextId = test_storage:getNextId(),
                    State = #stateRecord{id = NextId, deviceId = DeviceId, testId = TestId},
                    state_storage:updateState(State),
                    State;%инициализировались
                  _ -> Last
                end,
  ReturnState.

-spec getRandomTest/1 :: ([integer()]) -> testCase.
getRandomTest([TestId | _]) ->
  test_storage:getTest(TestId);

getRandomTest([]) ->
  NewTest = #testCase{description = "defaultTest", bits = [1, 2, 3, 4]},
  Id = test_storage:addTest(NewTest),
  test_storage:getTest(Id).


stop(Pid) ->
  try Pid ! stop of
    _ -> ok
  catch
    error : _ -> ok
  end
.